import java.util.ArrayList;
import java.util.Collection;
import java.util.HashMap;
import java.util.Iterator;
import java.util.List;
import java.util.Map;
import java.util.concurrent.LinkedBlockingQueue;

public class Library 
{
	public static final long DAYTIME = 5000; //in ms
	public static final long NIGHTITME = 3000; //in ms
	private enum Avaible { Open, Closed };
	private Avaible status;
	private int staffNumber = 0;
	private LinkedBlockingQueue<LibraryStaff> queue = 
			new LinkedBlockingQueue<LibraryStaff>();
	private Inventory inventory = new Inventory();
	private Map<Book, List<BookCopy>> storage =
			new HashMap<Book, List<BookCopy>>();
	private Map<Reader, List<BookLend>> booksTaken = 
			new HashMap<Reader, List<BookLend>>();
	
	
	public void registerReader(Reader r)
	{
		booksTaken.put(r, new ArrayList<BookLend>());
	}
	
	public void addBook(Book book) 
	{
		inventory.add(book);
		if(storage.containsKey(book))
		{
			List<BookCopy> temp= storage.get(book);
			temp.add(new BookCopy(book));
			storage.put(book,temp);
		}
		else 
		{
			List<BookCopy> list = new ArrayList<BookCopy>();
			list.add(new BookCopy(book));
			storage.put(book, list);
		}
	}

	public synchronized boolean isOpen()
	{
		return status == Avaible.Open;
	}

	public void open() 
	{
		Collection<List<BookLend>> cl = booksTaken.values();
		Iterator<List<BookLend>> it = cl.iterator();
		Iterator<BookLend> it2;
		BookLend temp;
		while(it.hasNext())
		{
			it2 =  it.next().iterator();
			while(it2.hasNext())
			{
				temp = it2.next();
				if(temp.passDay() <= 0)
					temp.getReader().onBookLate(temp);
			}
		}
		status = Avaible.Open;
		System.out.println("Library is now OPEN");
	}

	public void close() 
	{
		status = Avaible.Closed;
	}

	public int unregisterStaff(ILibraryStaff staff) 
	{
		queue.remove(staff);
		return --staffNumber;
	}

	public void registerStaff(LibraryStaff staff) 
	{
		try 
		{
			queue.put(staff);
			staffNumber++;
		} catch (InterruptedException e) { e.printStackTrace(); }
	}

	public void returnToDuty(LibraryStaff staff) 
	{
		try 
		{
			System.out.println("Hey, I[" + staff.name + "] just got back to duty");
			queue.put(staff);
			//wait();
		} 
		catch (InterruptedException e) { e.printStackTrace(); }
	}

	public LibraryStaff getFreeLibraryStaff() throws InterruptedException 
	{
		//notify();
		return queue.take();
	}

	public List<Book> getBooks(List<BookQuery> request) 
	{
		return inventory.decision(request);
	}

	public BookLend takeBook(List<Book> books, Reader reader) 
	{
		int size = 0;
		boolean hasAtLeastOneBook = false;
		for(BookLend bl : booksTaken.get(reader))
		{
			size += bl.getBooks().size();
		}
		if(size + books.size() > 10) return null;
		BookLend bl = new BookLend((byte)3,reader);
		for(Book book : books)
		{
			if(storage.containsKey(book))
				synchronized(storage.get(book))
				{
					for(BookCopy bc : storage.get(book))
					{
						if(bc.getStatus().equals(BookCopy.Status.Free))		
						{
							bl.addBookCopy(bc);
							hasAtLeastOneBook = true;
							//bc.setStatus(BookCopy.Status.takenForHome);
							break;
						}
					}
				}
		}
		if(hasAtLeastOneBook)
		{
			booksTaken.get(reader).add(bl);
			return bl;
		}
		return null;
	}

	public boolean returnBook(BookLend books, Reader reader) 
	{
		for(BookCopy book : books.getBooks())
		{
		
			synchronized(storage.get(book.getBook()))
			{
				for(BookCopy bc : storage.get(book.getBook()))
				{
					if(bc.getStatus().equals(BookCopy.Status.takenForHome))		
					{
						bc.setStatus(BookCopy.Status.Free);
						break;
					}
				}
			}
		}
		return booksTaken.get(reader).remove(books);
	}
	
}